﻿
namespace EasyNet.Solr.Impl
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Xml;

    using Commons;
    using Commons.Params;

    public class XmlUpdateParametersConvert : IUpdateParametersConvert<string>
    {
        private static readonly XmlWriterSettings settings = new XmlWriterSettings() { OmitXmlDeclaration = true };

        public string ConvertUpdateParameters(UpdateOptions updateOptions)
        {
            var stringBuilder = new StringBuilder();
            var xmlWriter = XmlTextWriter.Create(stringBuilder, settings);

            try
            {
                xmlWriter.WriteStartElement("stream");

                WriteDocs(xmlWriter, updateOptions.Docs, updateOptions.AddOptions);
                WriteDel(xmlWriter, updateOptions.DelById, updateOptions.DelByQ);
                WriteOptimizeOptions(xmlWriter, updateOptions.OptimizeOptions);
                WriteCommitOptions(xmlWriter, updateOptions.CommitOptions);

                xmlWriter.WriteEndElement();
            }
            catch
            {
                throw;
            }
            finally
            {
                xmlWriter.Close();
            }

            return stringBuilder.ToString();
        }

        public string ConvertRollbackParameters()
        {
            return string.Format("<{0}/>", UpdateParams.ROLLBACK);
        }

        private static void WriteDocs(XmlWriter xmlWriter, IEnumerable<SolrInputDocument> docs, AddOptions? addOptions)
        {
            if (docs != null && docs.Count() > 0)
            {
                xmlWriter.WriteStartElement("add");

                if (addOptions.HasValue)
                {
                    if (addOptions.Value.CommitWithin.HasValue && addOptions.Value.CommitWithin.Value > 0)
                    {
                        xmlWriter.WriteStartAttribute(UpdateParams.COMMITWITHIN);
                        xmlWriter.WriteValue(addOptions.Value.CommitWithin.Value);
                        xmlWriter.WriteEndAttribute();
                    }

                    if (addOptions.Value.Overwrite.HasValue)
                    {
                        xmlWriter.WriteStartAttribute(UpdateParams.OVERWRITE);
                        xmlWriter.WriteValue(addOptions.Value.Overwrite.Value ? "true" : "false");
                        xmlWriter.WriteEndAttribute();
                    }
                }

                foreach (var doc in docs)
                {
                    xmlWriter.WriteStartElement("doc");

                    if (doc.Boost != 1.0f)
                    {
                        xmlWriter.WriteStartAttribute("boost");
                        xmlWriter.WriteValue(doc.Boost);
                        xmlWriter.WriteEndAttribute();
                    }

                    foreach (SolrInputField field in doc.Values)
                    {
                        WriteSolrInputField(xmlWriter, field);
                    }

                    xmlWriter.WriteEndElement();
                }

                xmlWriter.WriteEndElement();
            }
        }

        private static void WriteSolrInputField(XmlWriter xmlWriter, SolrInputField field)
        {
            if (field == null)
            {
                return;
            }

            var value = field.Value;

            if (value == null)
            {
                return;
            }

            var name = field.Name;
            var boost = field.Boost;


            if (value is string || value is Enum || value is Guid || value is char || value is sbyte || value is byte || value is short || value is ushort || value is int || value is uint || value is long || value is ulong || value is decimal || value is double || value is float || value is bool || value is DateTime)
            {
                xmlWriter.WriteStartElement("field");

                xmlWriter.WriteStartAttribute("name");
                xmlWriter.WriteValue(name);
                xmlWriter.WriteEndAttribute();

                if (boost != 1.0f)
                {
                    xmlWriter.WriteStartAttribute("boost");
                    xmlWriter.WriteValue(boost);
                    xmlWriter.WriteEndAttribute();
                }

                xmlWriter.WriteValue(value is DateTime ? ((DateTime)value).ConvertToString() : value);
                xmlWriter.WriteEndElement();
            }
            else if (value is IEnumerable)
            {
                var valueArray = value as IEnumerable;

                foreach (var obj in valueArray)
                {
                    xmlWriter.WriteStartElement("field");

                    xmlWriter.WriteStartAttribute("name");
                    xmlWriter.WriteValue(name);
                    xmlWriter.WriteEndAttribute();

                    if (boost != 1.0f)
                    {
                        xmlWriter.WriteStartAttribute("boost");
                        xmlWriter.WriteValue(boost);
                        xmlWriter.WriteEndAttribute();
                    }

                    xmlWriter.WriteValue(obj is DateTime ? ((DateTime)obj).ConvertToString() : obj);
                    xmlWriter.WriteEndElement();
                }
            }
        }

        private static void WriteCommitOptions(XmlWriter xmlWriter, CommitOptions? commitOptions)
        {
            if (commitOptions.HasValue)
            {
                xmlWriter.WriteStartElement("commit");

                if (commitOptions.Value.WaitFlush.HasValue)
                {
                    xmlWriter.WriteStartAttribute(UpdateParams.WAIT_FLUSH);
                    xmlWriter.WriteValue(commitOptions.Value.WaitFlush.Value ? "true" : "false");
                    xmlWriter.WriteEndAttribute();
                }

                if (commitOptions.Value.WaitSearcher.HasValue)
                {
                    xmlWriter.WriteStartAttribute(UpdateParams.WAIT_SEARCHER);
                    xmlWriter.WriteValue(commitOptions.Value.WaitSearcher.Value ? "true" : "false");
                    xmlWriter.WriteEndAttribute();
                }

                if (commitOptions.Value.ExpungeDeletes.HasValue)
                {
                    xmlWriter.WriteStartAttribute(UpdateParams.EXPUNGE_DELETES);
                    xmlWriter.WriteValue(commitOptions.Value.ExpungeDeletes.Value ? "true" : "false");
                    xmlWriter.WriteEndAttribute();
                }

                xmlWriter.WriteEndElement();
            }
        }

        private static void WriteOptimizeOptions(XmlWriter xmlWriter, OptimizeOptions? optimizeOptions)
        {
            if (optimizeOptions.HasValue)
            {
                xmlWriter.WriteStartElement(UpdateParams.OPTIMIZE);

                if (optimizeOptions.Value.WaitFlush.HasValue)
                {
                    xmlWriter.WriteStartAttribute(UpdateParams.WAIT_FLUSH);
                    xmlWriter.WriteValue(optimizeOptions.Value.WaitFlush.Value ? "true" : "false");
                    xmlWriter.WriteEndAttribute();
                }

                if (optimizeOptions.Value.WaitSearcher.HasValue)
                {
                    xmlWriter.WriteStartAttribute(UpdateParams.WAIT_SEARCHER);
                    xmlWriter.WriteValue(optimizeOptions.Value.WaitSearcher.Value ? "true" : "false");
                    xmlWriter.WriteEndAttribute();
                }

                if (optimizeOptions.Value.MaxSegments.HasValue)
                {
                    xmlWriter.WriteStartAttribute(UpdateParams.MAX_OPTIMIZE_SEGMENTS);
                    xmlWriter.WriteValue(optimizeOptions.Value.MaxSegments.Value);
                    xmlWriter.WriteEndAttribute();
                }

                xmlWriter.WriteEndElement();
            }
        }

        private static void WriteDel(XmlWriter xmlWriter, IEnumerable<string> delById, IEnumerable<string> delByQ)
        {
            if ((delById != null && delById.Count() > 0) || (delByQ != null && delByQ.Count() > 0))
            {
                xmlWriter.WriteStartElement("delete");

                if (delById != null)
                {
                    foreach (var id in delById)
                    {
                        xmlWriter.WriteStartElement("id");
                        xmlWriter.WriteValue(id);
                        xmlWriter.WriteEndElement();
                    }
                }

                if (delByQ != null)
                {
                    foreach (var query in delByQ)
                    {
                        xmlWriter.WriteStartElement("query");
                        xmlWriter.WriteValue(query);
                        xmlWriter.WriteEndElement();
                    }
                }

                xmlWriter.WriteEndElement();
            }
        }
    }
}
